/* ----------------------------------------------------
@Time    : 2020/12/1 10:38 上午
@Author  : Hugo Yuan
@Email   : hugolxq@gmail.com
@File    : Requests.go
---------------------------------------------------- */
package RequestsV3

import (
	"bytes"
	"encoding/json"
	"github.com/sirupsen/logrus"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type IRequests interface {
	Get() (*Response, error)
	Post(string) (*Response, error)
	makeUrl() (string, error)
	newReq(method string, body io.Reader) (string, error)
	respHandle(method string, err error) (*Response, error)
}

type Requests struct {
	Client        *http.Client
	URL           string
	Headers       map[string]string
	Params        map[string]string
	Cookies       map[string]string
	Data          map[string]interface{}
	TimeOut       time.Duration
	Request       *http.Request
	Response      *Response
	Log           *logrus.Logger
	AllowRedirect bool
}

type IResponse interface {
	Content() []byte
	Text() string
	Map() (interface{}, error)
}

type Response struct {
	Response *http.Response
	content  []byte
}

type Option func(requests *Requests)

func (r *Requests) newReq(method string, body io.Reader) (string, error) {
	var err error

	// SetMethod
	r.Request.Method = method

	// SetBody
	rc, ok := body.(io.ReadCloser)
	if !ok && body != nil {
		rc = io.NopCloser(body)
	}
	r.Request.Body = rc

	// SetHeaders
	for k, v := range r.Headers {
		r.Request.Header.Add(k, v)
	}
	// SetCookies
	for k, v := range r.Cookies {
		cookie := &http.Cookie{Name: k, Value: v, HttpOnly: true}
		r.Request.AddCookie(cookie)
	}

	// Response
	// ------------------------------
	httpResp, err := r.Client.Do(r.Request)
	r.Response = &Response{Response: httpResp}
	return method, err
}

func (r *Requests) makeUrl() (string, error) {
	if len(r.Params) == 0 {
		return r.URL, nil
	}

	params := url.Values{}
	if r.Params == nil {
		return r.URL, nil
	}

	Url, err := url.Parse(r.URL)
	if err != nil {
		return "", err
	}

	// 添加params参数
	for k, v := range r.Params {
		params.Set(k, v)
	}

	Url.RawQuery = params.Encode()
	return Url.String(), nil
}

func (r *Requests) respHandle(method string, err error) (*Response, error) {
	if err != nil {
		return nil, err
	}

	r.Response.content, err = ioutil.ReadAll(r.Response.Response.Body)
	if err != nil {
		return nil, err
	}
	defer r.Response.Response.Body.Close()

	if r.Log != nil && r.Log.Level == logrus.DebugLevel {
		r.Log.Debugln("=============================================================")
		r.Log.Debugf("Request URL: %s", r.URL)
		r.Log.Debugf("Request Method: %s", method)
		//r.log.Debugf("Request Params: %s", r.Params)
		r.Log.Debugf("Request Data: %s", r.Data)
		r.Log.Debugf("Response: %s", string(r.Response.content))
		r.Log.Debugln("=============================================================")
	}
	return r.Response, err
}

func NewRequests(URL string, opts ...Option) *Requests {
	req, _ := http.NewRequest("GET", URL, nil)
	r := &Requests{
		URL:           URL,
		Headers:       make(map[string]string),
		AllowRedirect: true,
		Request:       req,
	}

	for _, opt := range opts {
		opt(r)
	}

	if r.AllowRedirect == false {
		r.Client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		}
	}

	if r.TimeOut == 0 {
		r.TimeOut = time.Second * 15
	}

	if r.Client == nil {
		r.Client = &http.Client{
			Transport: &http.Transport{DisableKeepAlives: true},
			Timeout:   r.TimeOut,
		}
	}
	return r
}

func SetLog(log *logrus.Logger) Option {
	return func(requests *Requests) {
		requests.Log = log
	}
}

func SetAllowRedirect(allowRedirect bool) Option {
	return func(requests *Requests) {
		requests.AllowRedirect = allowRedirect
	}
}

func SetClient(c *http.Client) Option {
	return func(requests *Requests) {
		requests.Client = c
	}
}

func (r *Requests) Get() (*Response, error) {
	URL, err := r.makeUrl()
	if err != nil {
		return nil, err
	}

	r.URL = URL

	//return r.respHandle(http.Get(URL))
	return r.respHandle(r.newReq("GET", nil))
}

// contentType 可以直接简写输入json或者form
func (r *Requests) Post(contentType string) (*Response, error) {
	URL, err := r.makeUrl()
	if err != nil {
		return nil, err
	}

	r.URL = URL

	if contentType == "application/x-www-form-urlencoded" || contentType == "form" {
		var f http.Request
		f.ParseForm()
		for k, v := range r.Data {
			f.Form.Add(k, GetString(v))
		}
		r.Headers["Content-Type"] = "application/x-www-form-urlencoded"
		//return r.respHandle(http.Post(URL, "application/x-www-form-urlencoded", strings.NewReader(f.Form.Encode())))
		return r.respHandle(r.newReq("POST", strings.NewReader(f.Form.Encode())))
	} else {
		jsonStr, err := json.Marshal(r.Data)
		if err != nil {
			return nil, err
		}
		r.Headers["Content-Type"] = "application/json"
		//return r.respHandle(http.Post(URL, "application/json", bytes.NewBuffer(jsonStr)))
		return r.respHandle(r.newReq("POST", bytes.NewBuffer(jsonStr)))
	}
}

func (resp *Response) Content() []byte {
	return resp.content
}

func (resp *Response) Text() string {
	return string(resp.Content())
}

func (resp *Response) Map() (interface{}, error) {
	var respMap interface{}
	err := json.Unmarshal(resp.Content(), &respMap)
	return respMap, err
}
